#include "app.hpp"
#include <cassert>
#include <iostream>
#include <iomanip>
#include <uuids.h>
#include <evcode.h>
#include "hrtext.hpp"
#include "mediatypeutil.hpp"

using std::wcout;
using std::endl;
using GraphUtil::IPinPtr;
using GraphUtil::IBaseFilterPtr;
//using GraphUtil::IFileSourceFilterPtr;
using GraphUtil::FindOutpinVideo;
using GraphUtil::FindOutpinAudio;
using GraphUtil::FindInpinVideo;
using GraphUtil::FindInpinAudio;

App::App(HANDLE hQuit) :
    m_hQuit(hQuit),
    m_input_filename(0)
{
    assert(m_hQuit);
}


int App::Parse(int argc, wchar_t* argv[])
{
    if (argc < 2)
    {
        wcout << "too few args" << endl;
        return 1;
    }
    
    if (argc > 2)
    {
        wcout << "too many args" << endl;
        return 1;
    }
    
    m_input_filename = argv[1];
    
    return 0;  //continue
}


int App::operator()(int argc, wchar_t* argv[])
{
    if (int status = Parse(argc, argv))
        return status;
        
    if (int status = CreateGraph())
        return status;
        
    const int status = RunGraph();
    
    DestroyGraph();
    
    return status;
}


int App::CreateGraph()
{
    assert(!bool(m_pGraph));
    
    HRESULT hr = m_pGraph.CreateInstance(CLSID_FilterGraphNoThread);
    assert(SUCCEEDED(hr));
    assert(bool(m_pGraph));
    
    const GraphUtil::IMediaFilterPtr pGraphFilter(m_pGraph);
    assert(bool(pGraphFilter));
    
    //see mkvsourcefilter.cpp
    //const wchar_t strSource[] = L"{A6E80BD1-6E49-46aa-89DC-F343FC8DDDF7}";
    //const IBaseFilterPtr pSource(strSource);
    const IBaseFilterPtr pSource(L"MJH.MkvSource");
    
    if (!bool(pSource))
    {
        wcout << "Unable to create MKVSource filter instance." << endl;
        return 1;
    }
    
    hr = m_pGraph->AddFilter(pSource, L"mkvsrc");
    assert(SUCCEEDED(hr));
    
    const GraphUtil::IFileSourceFilterPtr pFileSource(pSource);
    assert(bool(pFileSource));
    
    hr = pFileSource->Load(m_input_filename, 0);
    
    if (FAILED(hr))
    {
        wcout << "Unable to load input file \""
              << m_input_filename
              << "\"."
              << endl;
             
        return 1;
    }
    
    const GraphUtil::IGraphBuilderPtr pBuilder(m_pGraph);
    assert(bool(pBuilder));
    
    GraphUtil::IEnumPinsPtr e;

    hr = pSource->EnumPins(&e);
    assert(SUCCEEDED(hr));
    
    for (;;)
    {
        IPinPtr pin;
        
        hr = e->Next(1, &pin, 0);
        
        if (hr != S_OK)
            break;
    
        hr = pBuilder->Render(pin);
        assert(SUCCEEDED(hr));
    }
    
    return 0;  //success   
}



void App::DestroyGraph()
{
    if (IFilterGraph* pGraph = m_pGraph.Detach())
    {
        const ULONG n = pGraph->Release();
        assert(n == 0); n;
    }
}


int App::RunGraph()
{
    assert(bool(m_pGraph));
    
    const GraphUtil::IMediaEventPtr pEvent(m_pGraph);
    assert(bool(pEvent));

    HANDLE h;
    
    HRESULT hr = pEvent->GetEventHandle((OAEVENT*)&h);
    assert(hr == S_OK);
    assert(h);
    
    //DWORD dw = WaitForSingleObject(h, 0);
    
    enum { nh = 2 };
    const HANDLE ha[nh] = { m_hQuit, h };
    
    const GraphUtil::IMediaControlPtr pControl(m_pGraph);
    assert(bool(pControl));
    
    hr = pControl->Run();
    assert(SUCCEEDED(hr));
    
    //int n = 1;
    
    for (;;)
    {
        MSG msg;
        
        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
        {
            assert(msg.message != WM_QUIT);
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        
        const DWORD dw = MsgWaitForMultipleObjects(
                            nh,
                            ha,
                            0,
                            INFINITE,  //timeout (ms)
                            QS_ALLINPUT);
                    
        assert(dw >= WAIT_OBJECT_0);
        assert(dw <= (WAIT_OBJECT_0 + nh));
        
        if (dw == WAIT_OBJECT_0)  //quit
            break;
            
        if (dw == (WAIT_OBJECT_0 + nh))  //window message
            continue;
            
        //media event
        
        long code, param1, param2;
        
        HRESULT hr = pEvent->GetEvent(&code, &param1, &param2, 0);
        assert(hr == S_OK);
        
        hr = pEvent->FreeEventParams(code, param1, param2);
        assert(hr == S_OK);
        
        if (code == EC_USERABORT) //window closed
            break;
            
        if (code == EC_COMPLETE)
            break;
    }
    
    wcout << endl;
    
    hr = pControl->Stop();
    assert(SUCCEEDED(hr));
    
    return 0;
}
